home *** CD-ROM | disk | FTP | other *** search
- /* loadxm.c
- *
- * Generic Module Player Fasttracker 2 Module loader
- *
- * $Id: loadxm.c,v 1.12 1997/01/25 15:23:16 pekangas Exp $
- *
- * Copyright 1996,1997 Housemarque Inc.
- *
- * This file is part of the MIDAS Sound System, and may only be
- * used, modified and distributed under the terms of the MIDAS
- * Sound System license, LICENSE.TXT. By continuing to use,
- * modify or distribute this file you indicate that you have
- * read the license and understand and accept it fully.
- */
-
- /* Note! Loading an XM module requires INSANE amounts of memory for buffers
- - 90kb for pattern buffers (for 32 channels) plus the size of the largest
- sample for samples. */
-
- #include "lang.h"
- #include "mtypes.h"
- #include "errors.h"
- #include "mglobals.h"
- #include "mmem.h"
- #include "file.h"
- #include "sdevice.h"
- #include "gmplayer.h"
- #include "xm.h"
- #ifndef NOEMS
- #include "ems.h"
- #endif
- #include "mutils.h"
-
- #ifdef __WC32__
- #include <malloc.h>
- #endif
-
- #include <stdio.h>
-
- RCSID(const char *loadxm_rcsid = "$Id: loadxm.c,v 1.12 1997/01/25 15:23:16 pekangas Exp $";)
-
- /*#define CHECK_THE_HEAP*/
-
- extern ulong *__nheapbeg;
-
- /*#define DEBUGMESSAGES*/
-
-
- /****************************************************************************\
- *
- * Function: TestHeap(void)
- *
- * Description: Test if the heap is corrupted. Watcom C ONLY! Implemented here
- * because the Watcom _heapchk() often reports the heap is OK
- * even though it isn't.
- *
- * Returns: MIDAS error code
- *
- \****************************************************************************/
-
- #if defined(CHECK_THE_HEAP) && defined(__WC32__)
- /* Maximum valid block in heap: */
- #define MAXBLK (2048*1024)
-
- static int TestHeap(void)
- {
- static struct _heapinfo hinfo;
- int hstatus;
- unsigned len;
-
- hinfo._pentry = NULL;
-
- while ( 1 )
- {
- hstatus = _heapwalk(&hinfo);
- if ( hstatus != _HEAPOK )
- break;
- len = hinfo._size;
- if ( (len == 0) || (len > MAXBLK) || (len & 3) )
- return errHeapCorrupted;
- }
-
- if ( (hstatus == _HEAPEND) || (hstatus == _HEAPEMPTY) )
- return OK;
- return errHeapCorrupted;
- }
- #define CHECKHEAP \
- { \
- if ( TestHeap() != OK ) \
- { \
- puts("HEAP CORRUPTED!"); \
- ERROR(errHeapCorrupted, ID_gmpLoadXM); \
- LoadError(); \
- return errHeapCorrupted; \
- } \
- }
-
- #else
- #define CHECKHEAP
- #endif
-
- /* Pass error code in variable "error" on, used in gmpLoadXM(). */
- #define PASSERR() { LoadError(); PASSERROR(ID_gmpLoadXM) }
-
-
- /* Conversion table from FT2 command number to GMP ones: */
- static uchar ft2Commands[36] =
- {
- gmpcArpeggio, /* 0 - Arpeggio */
- gmpcSlideUp, /* 1 - Portamento up */
- gmpcSlideDown, /* 2 - Portamento down */
- gmpcTonePortamento, /* 3 - Tone portamento */
- gmpcVibrato, /* 4 - Vibrato */
- gmpcTPortVSlide, /* 5 - Tone portamento + volume slide */
- gmpcVibVSlide, /* 6 - Vibrato + volume slide */
- gmpcTremolo, /* 7 - Tremolo */
- gmpcSetPanning, /* 8 - Set panning (extension) */
- gmpcSampleOffset, /* 9 - Sample offset */
- gmpcVolumeSlide, /* A - Volume slide */
- gmpcPositionJump, /* B - Position jump */
- gmpcSetVolume, /* C - Set volume */
- gmpcPatternBreak, /* D - Pattern break */
- gmpcNone, /* E - E-commands (see below) */
- gmpcSetSpeed, /* F - Set speed/tempo */
- gmpcSetMVolume, /* G - Set master volume */
- gmpcMVolSlide, /* H - Master volume slide */
- gmpcNone, /* I - Unused */
- gmpcNone, /* J - Unused */
- gmpcNone, /* K - Unused */
- gmpcNone, /* L - Set envelope position */
- gmpcNone, /* M - Unused */
- gmpcNone, /* N - Unused */
- gmpcNone, /* O - Unused */
- gmpcPanSlide, /* P - Panning slide */
- gmpcNone, /* Q - Unused */
- gmpcS3MRetrig, /* R - ST3 multi retrig */
- gmpcNone, /* S - Unused */
- gmpcNone, /* T - Tremor */
- gmpcNone, /* U - Unused */
- gmpcNone, /* V - Unused */
- gmpcMusicSync, /* W - Music Synchronization (extension) */
- gmpcNone, /* Extra fine slides - handled separately */
- gmpcNone, /* Y - Unused */
- gmpcNone, /* Z - Unused */
- };
-
- /* Conversion table from FT2 E-command numbers to GMP commands: */
- static uchar ft2ECommands[16] =
- {
- gmpcNone, /* E0 - Toggle filter */
- gmpcFineSlideUp, /* E1 - Fine portamento up */
- gmpcFineSlideDown, /* E2 - Fine portamento down */
- gmpcNone, /* E3 - Set glissando control */
- gmpcNone, /* E4 - Set vibrato type */
- gmpcNone, /* E5 - Set finetune */
- gmpcPatternLoop, /* E6 - Pattern loop */
- gmpcNone, /* E7 - Set tremolo type */
- gmpcSetPanning16, /* E8 - Set panning (extension) */
- gmpcPTRetrig, /* E9 - Retrig note */
- gmpcFineVolSlideUp, /* EA - Fine volume slide up */
- gmpcFineVolSlideDown, /* EB - Fine volume slide down */
- gmpcNoteCut, /* EC - Note cut */
- gmpcNoteDelay, /* ED - Note delay */
- gmpcPatternDelay, /* EE - Pattern delay */
- gmpcNone /* EF - Invert loop */
- };
-
-
-
-
- /****************************************************************************\
- * Module loader buffers and file handle. These variables are static
- * instead of local so that a separate deallocation can be used which
- * will be called before exiting in error situations
- \****************************************************************************/
- static fileHandle f; /* file handle for module file */
- static int fileOpened; /* 1 if module file has been opened */
- static gmpModule *module; /* pointer to GMP module structure */
- static xmHeader *header; /* pointer to FT2 module header */
- static uchar *instUsed; /* instrument used flags */
- static xmPattern *xmPatt; /* pointer to XM pattern data */
- static gmpPattern *convPatt; /* pointer to converted pattern data*/
- static uchar *smpBuf; /* sample loading buffer */
- static unsigned numChans; /* number of channels in module */
- static xmInstHeader *xmInst; /* XM instrument header */
- static xmInstSampleHeader *xmInstSmp; /* XM instrument sample header */
- static xmSampleHeader *xmSample; /* XM sample header */
- static int maxInstUsed; /* maximum used instrument number */
-
-
-
-
-
- /****************************************************************************\
- *
- * Function: int ConvertPattern(xmPattern *srcPatt, gmpPattern *destPatt,
- * unsigned *convLen);
- *
- * Description: Converts a pattern from FT2 to GMP internal format.
- * Also updates instUsed flags.
- *
- * Input: xmPattern *srcPatt pointer to pattern in FT2 format
- * gmpPattern *destPatt pointer to destination GMP pattern
- * unsigned *convLen pointer to converted pattern length
- *
- * Returns: MIDAS error code. Converted pattern length (in bytes,
- * including header) is written to *convLen.
- *
- \****************************************************************************/
-
- static int ConvertPattern(xmPattern *srcPatt, gmpPattern *destPatt,
- unsigned *convLen)
- {
- uchar *src = &srcPatt->data[0];
- uchar *dest = &destPatt->data[0];
- unsigned row, chan;
- unsigned len;
- uchar note, inst, command, infobyte, vol, compInfo;
- uchar xmComp;
- uchar gmpCommand;
-
- /*
- ***********************
- * Pattern format: *
- ***********************
-
- The patterns are stored as ordinary MOD patterns, except that each
- note is stored as 5 bytes:
-
- ? 1 (byte) Note (0-71, 0 = C-0)
- +1 1 (byte) Instrument (0-128)
- +2 1 (byte) Volume column byte (see below)
- +3 1 (byte) Effect type
- +4 1 (byte) Effect parameter
-
- A simle packing scheme is also adopted, so that the patterns not become
- TOO large: Since the MSB in the note value is never used, if is used for
- the compression. If the bit is set, then the other bits are interpreted
- as follows:
-
- bit 0 set: Note follows
- 1 set: Instrument follows
- 2 set: Volume column byte follows
- 3 set: Effect follows
- 4 set: Guess what!
- */
-
- if ( srcPatt->numRows > 256 || srcPatt->numRows == 0 )
- return errInvalidModule;
-
- /* Check if the pattern is empty: */
- if ( srcPatt->pattDataSize == 0 )
- {
- /* Write row end marker for each row: */
- for ( row = 0; row < srcPatt->numRows; row++ )
- *(dest++) = 0;
-
- /* Write number of rows to pattern header: */
- destPatt->rows = srcPatt->numRows;
-
- /* Write converted pattern length to header and return it in
- *convLen: */
- *convLen = destPatt->length = srcPatt->numRows + sizeof(gmpPattern);
- return OK;
- }
-
- for ( row = 0; row < srcPatt->numRows; row++ )
- {
- for ( chan = 0; chan < numChans; chan++ )
- {
- /* No data for this channel yet: */
- compInfo = 0;
- note = 0xFF;
- inst = 0xFF;
- command = 0;
- infobyte = 0;
- vol = 0;
- gmpCommand = 0;
-
- /* Get note number/compression info from pattern data: */
- xmComp = *src;
-
- /* If the most significant bit in the read byte is 1 the byte
- contains compression info bits, otherwise it is the note
- number and all fields are present: */
- if ( xmComp & 0x80 )
- {
- /* The byte is compression info - continue from next byte: */
- src++;
- xmComp &= 0x7F;
- }
- else
- {
- /* The byte is note number - mark that all fields are present
- and use current byte as the note number: */
- xmComp = 0x7F;
- }
-
- /* Check if there is a new note: */
- if ( xmComp & 1 )
- {
- /* Check that the note is not key off and convert it to GMP
- internal format: */
- if ( *src > 96 )
- note = 0xFE;
- else
- note = ((((*src)-1) / 12) << 4) | (((*src)-1) % 12);
-
- src++;
-
- /* Mark that there is a new note: */
- compInfo |= 32;
- }
-
- /* Check if there is a new instrument: */
- if ( xmComp & 2 )
- {
- /* Get the instrument number: */
- inst = (*(src++)) - 1;
-
- /* [Petteri] 06 Dec 1997 - Why was this here?
- if ( note == 0xFE )
- inst = 0xFF;
- */
-
- if ( inst != 0xFF )
- {
- /* Check if the instrument number if above maximum instrument
- number used: */
- if ( inst > maxInstUsed )
- maxInstUsed = inst;
-
- /* Mark instrument used: */
- instUsed[inst] = 1;
-
- /* Mark that there is a new instrument: */
- compInfo |= 32;
- }
- }
-
- /* Check if there is a volume column byte: */
- if ( xmComp & 4 )
- {
- /* Get the volume column byte: */
- vol = *(src++);
-
- /* Ignore the volume column byte if it is zero: */
- if ( vol != 0 )
- {
- /* Convert the byte to volume number if it is a set
- volume command: */
- if ( (vol >= 0x10) && (vol <= 0x50) )
- vol -= 0x10;
-
- /* Mark that there is a volume column byte: */
- compInfo |= 64;
- }
- }
-
- /* Check if there is a command: */
- if ( xmComp & 8 )
- {
- /* Get the command number: */
- command = *(src++);
- }
-
- /* Check if there is an infobyte: */
- if ( xmComp & 16 )
- {
- /* Get the infobyte: */
- infobyte = *(src++);
- }
-
- /* If there is a command or an infobyte we need to write the
- command to the destination pattern: */
- if ( (command != 0) || (infobyte != 0) )
- {
- /* Convert command to GMP command number: */
- gmpCommand = ft2Commands[command];
-
- /* Special commands */
- switch ( command )
- {
- case 0x0E:
- /* Command E - check actual command number: */
- gmpCommand = ft2ECommands[infobyte >> 4];
- infobyte = infobyte & 0x0F;
- break;
-
- case 0x0F:
- /* Fasttracker 2 command 0Fh - set speed or set tempo.
- If infobyte >= 32, the BPM tempo is changed: */
- if ( infobyte >= 32 )
- gmpCommand = gmpcSetTempo;
- else
- gmpCommand = gmpcSetSpeed;
- break;
-
- case ('K' - 'F' + 0x0F):
- /* FT2 command K - release note. Convert to release
- note note number: */
- note = 254;
- compInfo |= 32;
- gmpCommand = gmpcNone;
- break;
-
- case ('X' - 'F' + 0x0F):
- /* FT2 command X - eXtra fine slides. Convert
- to separate commands: */
- gmpCommand = gmpcNone;
- if ( (infobyte & 0xF0) == 0x10 )
- gmpCommand = gmpcExtraFineSlideUp;
- if ( (infobyte & 0xF0) == 0x20 )
- gmpCommand = gmpcExtraFineSlideDown;
-
- infobyte = infobyte & 0x0F;
- break;
- }
-
- /* Mark that there is a command: */
- if ( gmpCommand != gmpcNone )
- compInfo |= 128;
- }
-
- /* If the compression information is nonzero, there is some
- data for this channel: */
- if ( compInfo != 0 )
- {
- /* Set channel number to lower 5 bits of the compression info
- and write it to destination: */
- compInfo |= chan;
- *(dest++) = compInfo;
-
- /* Check if there a note+instrument pair: */
- if ( compInfo & 32 )
- {
- /* Write note and instrument numbers: */
- *(dest++) = note;
- *(dest++) = inst;
- }
-
- /* Check if there is a volume column byte: */
- if ( compInfo & 64 )
- {
- /* Write the volume column byte: */
- *(dest++) = vol;
- }
-
- /* Check if there is a command: */
- if ( compInfo & 128 )
- {
- /* Write command and command infobyte: */
- *(dest++) = gmpCommand;
- *(dest++) = infobyte;
- }
- }
- }
-
- /* Write row end marker: */
- *(dest++) = 0;
- }
-
- /* Write number of rows to pattern header: */
- destPatt->rows = srcPatt->numRows;
-
- /* Calculate converted pattern length: */
- len = (unsigned) (dest - ((uchar*) destPatt));
-
- /* Write converted pattern length to header and return it in *convLen: */
- destPatt->length = len;
- *convLen = len;
-
- return OK;
- }
-
-
-
-
- /****************************************************************************\
- *
- * Function: void LoadError(void)
- *
- * Description: Stops loading the module, deallocates all buffers and closes
- * the file.
- *
- \****************************************************************************/
-
- #define condFree(x) { if ( x != NULL ) if ( memFree(x) != OK) return; }
-
- static void LoadError(void)
- {
- /* Close file if opened. Do not process errors. */
- if ( fileOpened )
- if ( fileClose(f) != OK )
- return;
-
- /* Attempt to deallocate module if allocated. Do not process errors. */
- if ( module != NULL )
- if ( gmpFreeModule(module) != OK )
- return;
-
- /* Deallocate structures if allocated: */
- condFree(header)
- condFree(instUsed)
- condFree(smpBuf)
- condFree(xmInst)
- condFree(xmInstSmp)
- condFree(xmSample)
- }
-
-
-
-
- /****************************************************************************\
- *
- * Function: int gmpLoadXM(char *fileName, int addSD, mpModule **module)
- *
- * Description: Loads a Fasttracker 2 module to memory in Generic Module
- * Player module format
- *
- * Input: char *fileName module file name
- * int addSD 1 if module samples should be added to
- * the current Sound Device, 0 if not
- * int (*SampleCallback)(...) pointer to callback function that
- * will be called after sample has been
- * added to Sound Device, but before
- * sample data is deallocated
- * mpModule **module pointer to GMP module pointer
- *
- * Returns: MIDAS error code
- *
- \****************************************************************************/
-
- int CALLING gmpLoadXM(char *fileName, int addSD, int
- (CALLING *SampleCallback)(sdSample *sdsmp, gmpSample *gmpsmp),
- gmpModule **_module)
- {
- int error; /* MIDAS error code */
- static gmpInstrument *inst;
- unsigned i, s;
- unsigned n;
- unsigned numSamplesUsed;
- ulong maxSample;
- static unsigned convPattLen;
- static long filePos;
- static gmpSample *sample;
- static sdSample sdSmp;
- ulong smpTotal;
- long smpStart;
- long skip;
- gmpEnvelope *env;
- uchar *smpData;
- int d;
- unsigned numInsts;
- unsigned maxPatt;
- #ifndef NOEMS
- uchar *patt;
- #endif
-
-
- /* point buffers to NULL and set fileOpened to 0 so that LoadError()
- can be called at any point: */
- fileOpened = 0;
- module = NULL;
- header = NULL;
- instUsed = NULL;
- xmPatt = NULL;
- convPatt = NULL;
- smpBuf = NULL;
- xmInst = NULL;
- xmInstSmp = NULL;
- xmSample = NULL;
-
- #ifdef DEBUGMESSAGES
- puts("Loading header");
- #endif
-
- /* Allocate memory for XM module header: */
- if ( (error = memAlloc(sizeof(xmHeader), (void**) &header)) != OK )
- PASSERR()
-
- /* Open module file: */
- if ( (error = fileOpen(fileName, fileOpenRead, &f)) != OK )
- PASSERR()
- fileOpened = 1;
-
- /* Allocate memory for the module structure: */
- if ( (error = memAlloc(sizeof(gmpModule), (void**) &module)) != OK )
- PASSERR()
-
- /* Clear module structure so that it can safely be deallocated with
- gmpFreeModule() at any point: */
- module->panning = NULL;
- module->songData = NULL;
- module->instruments = NULL;
- module->patterns = NULL;
-
- /* Read Fasttracker module header: */
- if ( (error = fileRead(f, header, sizeof(xmHeader))) != OK )
- PASSERR()
-
- /* Skip possible unused bytes at the end of the module header: */
- if ( (error = fileSeek(f, header->hdrSize - sizeof(xmHeader) + 60,
- fileSeekRelative)) != OK )
- PASSERR()
-
- /* Check that the header contains a valid signature and the file format
- version number is correct: */
- if ( (!mMemEqual(header->signature, "Extended Module: ", 17)) ||
- (header->version < 0x104) )
- {
- ERROR(errInvalidModule, ID_gmpLoadXM);
- LoadError();
- return errInvalidModule;
- }
-
- /* Get number of channels from the header: */
- numChans = header->numChans;
- module->numChannels = numChans;
-
- /* Copy song name: */
- mMemCopy(module->name, header->name, 20);
- module->name[20] = 0; /* force terminating '\0' */
-
- module->songLength = header->songLength; /* copy song length */
- module->playMode = gmpFT2; /* set FT2 playing mode */
- module->masterVolume = 64; /* maximum master volume */
- module->speed = (uchar) header->speed; /* copy initial speed */
- module->tempo = (uchar) header->tempo; /* copy initial tempo */
- module->restart = header->restart; /* copy restart position */
- module->numPatts = header->numPatts; /* copy number of patterns */
-
- /* Check if linear frequencies are used: */
- if ( header->flags & xmLinearFreq )
- module->playFlags.linFreqTable = 1;
- else
- module->playFlags.linFreqTable = 0;
-
- /* Allocate memory for channel initial panning positions: */
- if ( (error = memAlloc(32 * sizeof(int), (void**) &module->panning))
- != OK )
- PASSERR()
-
- /* Set initial panning position to all middle: */
- for ( i = 0; i < numChans; i++ )
- module->panning[i] = 0x80;
-
- /* Allocate memory for song data: */
- if ( (error = memAlloc(sizeof(ushort) * module->songLength,
- (void**) &module->songData)) != OK )
- PASSERR()
-
- /* Copy song data and find the maximum pattern used: */
- maxPatt = 0;
- for ( i = 0; i < module->songLength; i++ )
- {
- module->songData[i] = header->orders[i];
- if ( module->songData[i] > maxPatt )
- maxPatt = module->songData[i];
- }
-
- /* If more patterns are used than there are in the file, allocate memory
- for all of them: */
- if ( maxPatt >= module->numPatts )
- module->numPatts = maxPatt + 1;
-
- /* Allocate memory for pattern pointers: */
- if ( (error = memAlloc(sizeof(gmpPattern*) * module->numPatts,
- (void**) &module->patterns)) != OK )
- PASSERR()
-
- /* Set all pattern pointers to NULL to mark them unallocated: */
- for ( i = 0; i < module->numPatts; i++ )
- module->patterns[i] = NULL;
-
- /* Allocate memory for instrument used flags: */
- if ( (error = memAlloc(256, (void**)&instUsed)) != OK )
- PASSERR()
-
- /* Mark all instruments unused: */
- for ( i = 0; i < 256; i++ )
- instUsed[i] = 0;
-
- /* Clear maximum instrument number used: */
- maxInstUsed = 0;
-
- /* Allocate memory for pattern loading buffer: */
- if ( (error = memAlloc(sizeof(xmPattern) + numChans * 5 * 256,
- (void**) &xmPatt)) != OK )
- PASSERR()
-
- /* Allocate memory for pattern conversion buffer: (maximum GMP pattern
- data size is 6 bytes per row per channel plus header) */
- if ( (error = memAlloc(numChans * 256 * 6 + sizeof(gmpPattern),
- (void**) &convPatt)) != OK )
- PASSERR()
-
- #ifdef DEBUGMESSAGES
- puts("Starting to load patterns");
- #endif
-
- /* Load and convert all patterns: */
- for ( i = 0; i < module->numPatts; i++ )
- {
- #ifdef DEBUGMESSAGES
- printf("Pattern %i - ", i);
- #endif
-
- CHECKHEAP
-
- /* Check if this pattern is in file: */
- if ( i < header->numPatts )
- {
- /* Read pattern header: */
- if ( (error = fileRead(f, xmPatt, sizeof(xmPattern))) != OK )
- PASSERR()
-
- /* Skip possible unused bytes at the end of the pattern header: */
- if ( (error = fileSeek(f, xmPatt->headerLength - sizeof(xmPattern),
- fileSeekRelative)) != OK )
- PASSERR()
-
- #ifdef DEBUGMESSAGES
- printf("sizeof(xmPattern) = %i - ", sizeof(xmPattern));
- printf("offsetti = %u - ",
- (unsigned) &xmPatt->numRows - (unsigned) xmPatt);
- printf("Rows: %u, size %u, bsize %u - ", xmPatt->numRows,
- xmPatt->pattDataSize, 5 * 256 * numChans);
- #endif
-
- /* Read pattern data: */
- if ( xmPatt->pattDataSize != 0 )
- {
- if ( (error = fileRead(f, xmPatt->data, xmPatt->pattDataSize))
- != OK )
- PASSERR()
- }
- }
- else
- {
- /* Pattern used but not in file - mark empty: */
- xmPatt->pattDataSize = 0;
- xmPatt->numRows = 64;
-
- #ifdef DEBUGMESSAGES
- printf("Empty -");
- #endif
- }
-
- CHECKHEAP
-
- #ifdef DEBUGMESSAGES
- printf("Loaded, about to convert - ");
- #endif
-
- /* Convert the pattern data, checking the instruments used: */
- if ( (error = ConvertPattern(xmPatt, convPatt, &convPattLen))
- != OK )
- PASSERR()
-
- #ifdef DEBUGMESSAGES
- printf("Conv. size %u - ", convPattLen);
- #endif
-
- CHECKHEAP
-
- #ifndef NOEMS
- if ( mUseEMS == 1 ) /* is EMS memory used? */
- {
- /* Allocate EMS for converted pattern data for current pattern in
- module: */
- if ( (error = emsAlloc(convPattLen, (emsBlock**)
- &module->patterns[i])) != OK )
- PASSERR()
-
- /* Map the allocated EMS block to conventional memory: */
- if ( (error = emsMap((emsBlock*) module->patterns[i],
- (void**) &patt)) != OK)
- PASSERR()
-
- /* Copy the converted pattern data to the EMS block: */
- mMemCopy(patt, convPatt, convPattLen);
- }
- else
- #endif
- {
- /* Allocate memory for converted pattern data for current pattern
- in module: */
- if ( (error = memAlloc(convPattLen, (void**)&module->patterns[i]))
- != OK )
- PASSERR()
-
- /* Copy the converted pattern data to the EMS block: */
- mMemCopy(module->patterns[i], convPatt, convPattLen);
- }
- #ifdef DEBUGMESSAGES
- puts("OK");
- #endif
- CHECKHEAP
- }
-
- #ifdef DEBUGMESSAGES
- puts("Patterns loaded");
- #endif
-
- CHECKHEAP
-
- /* Deallocate pattern conversion buffer: */
- if ( (error = memFree(convPatt)) != OK )
- PASSERR()
- convPatt = NULL;
-
- /* Deallocate pattern loading buffer: */
- if ( (error = memFree(xmPatt)) != OK )
- PASSERR()
- xmPatt = NULL;
-
- /* Check actual number of instruments needed: (ensure that all used
- instruments are added to Sound Device) */
- if ( maxInstUsed >= header->numInsts )
- numInsts = maxInstUsed + 1;
- else
- numInsts = header->numInsts;
- module->numInsts = numInsts;
-
- #ifdef DEBUGMESSAGES
- printf("maxInstUsed = %i\n", maxInstUsed);
- #endif
-
-
- /* Now we read all instrument and sample headers from the files,
- converting them to GMPlayer structures. Offsets to sample data in the
- file are saved in GMPlayer sample structure sample pointers (always
- at least 32-bit) so that they can be read faster. This is necessary
- as we need to be able to find out the maximum sample length to be able
- to allocate a loading buffer for the samples. */
-
-
- /* Allocate memory for instrument pointers: */
- if ( (error = memAlloc(sizeof(gmpInstrument*) * numInsts,
- (void**) &module->instruments)) != OK )
- PASSERR()
-
- /* Set all instrument pointers to NULL to mark them unallocated: */
- for ( i = 0; i < numInsts; i++ )
- module->instruments[i] = NULL;
-
- /* Allocate memory for instrument and sample headers: */
- if ( (error = memAlloc(sizeof(xmInstHeader), (void**) &xmInst)) != OK )
- PASSERR()
- if ( (error = memAlloc(sizeof(xmInstSampleHeader), (void**) &xmInstSmp))
- != OK )
- PASSERR()
- if ( (error = memAlloc(sizeof(xmSampleHeader), (void**) &xmSample))
- != OK )
- PASSERR()
-
- maxSample = 0;
-
- for ( i = 0; i < numInsts; i++ )
- {
- #ifdef DEBUGMESSAGES
- printf("Loading instrument %i\n", i);
- #endif
-
- /* Check if the instrument number is below XM header maximum. If so,
- load the instrument header, otherwise fill in necessary information
- so that it appears like an empty header (which it is). Ugly? You
- bet! */
- if ( i < header->numInsts )
- {
- /* Read instrument header: */
- if ( (error = fileRead(f, xmInst, sizeof(xmInstHeader))) != OK )
- PASSERR()
-
- skip = xmInst->instSize - sizeof(xmInstHeader);
-
- /* If the instrument has samples read instrument sample header: */
- if ( xmInst->numSamples != 0 )
- {
- if ( (error = fileRead(f, xmInstSmp, sizeof(xmInstSampleHeader)))
- != OK )
- PASSERR()
-
- skip -= sizeof(xmInstSampleHeader);
-
- /* Check number of samples used: */
- numSamplesUsed = 0;
- for ( n = 0; n < 96; n++ )
- {
- if ( xmInstSmp->noteSmpNums[n] >= numSamplesUsed )
- numSamplesUsed = xmInstSmp->noteSmpNums[n] + 1;
- }
- }
- else
- numSamplesUsed = 0;
- }
- else
- {
- /* Instrument not in the file is used - clear the structure: */
- xmInst->instSize = 0;
- xmInst->instName[0] = 0;
- xmInst->instType = 0;
- xmInst->numSamples = 0;
- numSamplesUsed = 0;
-
- /* And how we love Triton! */
- mMemCopy(&xmInst->instName[1], "S2 loves Triton!", 17);
-
- /* No data after the header: (what header? ;) */
- skip = 0;
- }
-
-
- /* If instrument is used, it must have at least one sample (regardless
- of what the XM instrument header says) */
- if ( instUsed[i] )
- {
- if ( numSamplesUsed > 0 )
- n = numSamplesUsed;
- else
- n = 1;
- }
- else
- {
- n = 0;
- }
-
- /* Allocate memory for instrument and sample structures: */
- if ( (error = memAlloc(sizeof(gmpInstrument) + n*sizeof(gmpSample),
- (void**) &inst))
- != OK )
- PASSERR()
- module->instruments[i] = inst;
-
- /* Copy instrument name: */
- mMemCopy(inst->name, xmInst->instName, 22);
- inst->name[22] = 0;
-
- inst->numSamples = n;
- inst->used = instUsed[i];
-
- /* Clear all allocated samples so that the module can be deallocated
- safely: */
- sample = &inst->samples[0];
- for ( s = 0; s < n; s++ )
- {
- sample->sdHandle = 0;
- sample->sample = NULL;
- sample->sampleType = smpNone;
- sample->volume = 0;
- sample->panning = panMiddle;
- sample->baseTune = 0;
- sample->finetune = 0;
- }
-
- inst->noteSamples = NULL;
- inst->volEnvelope = NULL;
- inst->panEnvelope = NULL;
-
- /* Process instrument sample header: */
- if ( xmInst->numSamples > 0 )
- {
- /* Check if we need a note sample number table: */
- if ( numSamplesUsed > 1 )
- {
- /* Allocate memory for table: */
- if ( (error = memAlloc(96, (void**) &inst->noteSamples))
- != OK )
- PASSERR()
-
- /* Copy note numbers: */
- mMemCopy(inst->noteSamples, xmInstSmp->noteSmpNums, 96);
-
- #ifdef DEBUGMESSAGES
- printf("\nnoteSamples!\n");
- #endif
- }
-
- /* Check if volume envelope is used: */
- if ( xmInstSmp->volEnvFlags & xmEnvelopeOn )
- {
- /* Allocate memory for volume envelope info: */
- if ( (error = memAlloc(sizeof(gmpEnvelope), (void**)
- &inst->volEnvelope)) != OK )
- PASSERR()
- env = inst->volEnvelope;
-
- /* Copy number of points in envelope: */
- env->numPoints = xmInstSmp->numVolPoints;
-
- /* Set sustain point: */
- if ( xmInstSmp->volEnvFlags & xmEnvelopeSustain )
- env->sustain = xmInstSmp->volSustain;
- else
- env->sustain = -1;
-
- /* Set loop start and end points: (FT2 ignores envelope
- loops where the start and end positions are the same) */
- if ( (xmInstSmp->volEnvFlags & xmEnvelopeLoop) &&
- (xmInstSmp->volLoopStart != xmInstSmp->volLoopEnd) )
- {
- env->loopStart = xmInstSmp->volLoopStart;
- env->loopEnd = xmInstSmp->volLoopEnd;
- }
- else
- {
- env->loopStart = env->loopEnd = -1;
- }
-
- /* Copy envelope points: */
- mMemCopy(env->points, xmInstSmp->volEnvelope, 48);
- }
- else
- inst->volEnvelope = NULL;
-
- /* Check if panning envelope is used: */
- if ( xmInstSmp->panEnvFlags & xmEnvelopeOn )
- {
- /* Allocate memory for volume envelope info: */
- if ( (error = memAlloc(sizeof(gmpEnvelope), (void**)
- &inst->panEnvelope)) != OK )
- PASSERR()
- env = inst->panEnvelope;
-
- /* Copy number of points in envelope: */
- env->numPoints = xmInstSmp->numPanPoints;
-
- /* Set sustain point: */
- if ( xmInstSmp->panEnvFlags & xmEnvelopeSustain )
- env->sustain = xmInstSmp->panSustain;
- else
- env->sustain = -1;
-
- /* Set loop start and end points: (FT2 ignores envelope
- loops where the start and end positions are the same) */
- if ( (xmInstSmp->panEnvFlags & xmEnvelopeLoop) &&
- (xmInstSmp->panLoopStart != xmInstSmp->panLoopEnd) )
- {
- env->loopStart = xmInstSmp->panLoopStart;
- env->loopEnd = xmInstSmp->panLoopEnd;
- }
- else
- {
- env->loopStart = env->loopEnd = -1;
- }
-
- /* Copy envelope points: */
- mMemCopy(env->points, xmInstSmp->panEnvelope, 48);
- }
- else
- inst->panEnvelope = NULL;
-
- /* Copy volume fade-out speed: */
- inst->volFadeout = xmInstSmp->volFadeout;
-
- /* Copy instrument auto-vibrato information: */
- inst->vibType = xmInstSmp->vibType;
- inst->vibSweep = xmInstSmp->vibSweep;
- inst->vibDepth = xmInstSmp->vibDepth;
- inst->vibRate = xmInstSmp->vibRate;
- }
-
- /* Skip possible unnecessary bytes at the end of the instrument
- header: */
- if ( (error = fileSeek(f, skip, fileSeekRelative)) != OK )
- PASSERR()
-
- /* Get current file position: */
- if ( (error = fileGetPosition(f, &filePos)) != OK )
- PASSERR()
-
- /* Calculate start offset for first sample: */
- smpStart = filePos + xmInst->numSamples * xmInstSmp->headerSize;
-
- /* Total sample length for this instrument: */
- smpTotal = 0;
-
- /* Load and convert all sample headers and calculate total sample
- length: */
- sample = &inst->samples[0];
- for ( s = 0; s < xmInst->numSamples; s++ )
- {
- /* Read sample header: */
- if ( (error = fileRead(f, xmSample, sizeof(xmSampleHeader)))
- != OK )
- PASSERR()
-
- /* Convert sample header if it used: */
- if ( s < inst->numSamples )
- {
- /* Point sample->fileOffset to sample data start: */
- sample->fileOffset = smpStart;
-
- /* Copy sample name: */
- mMemCopy(sample->name, xmSample->smpName, 22);
- sample->name[22] = 0;
-
- /* Copy sample information: */
- sample->sampleLength = xmSample->smpLength;
- sample->loop1Start = xmSample->loopStart;
- sample->loop1End = xmSample->loopStart + xmSample->loopLength;
- sample->volume = xmSample->volume;
- sample->panning = (int) ((unsigned) xmSample->panning);
- sample->baseTune = xmSample->relNote;
- sample->finetune = (int) ((signed char) xmSample->finetune);
- sample->loop2Type = loopNone;
-
- /* Convert sample type: */
- if ( xmSample->flags & xmSample16bit )
- sample->sampleType = smp16bit;
- else
- sample->sampleType = smp8bit;
-
- /* Update maximum sample length: */
- if ( sample->sampleLength > maxSample )
- maxSample = sample->sampleLength;
-
- /* Convert sample loop type information: */
- switch ( xmSample->flags & 3 )
- {
- case 1:
- /* Unidirectional loop: */
- sample->loopMode = sdLoop1;
- sample->loop1Type = loopUnidir;
- break;
-
- case 2:
- /* Bidirectional loop: */
- sample->loopMode = sdLoop1;
- sample->loop1Type = loopBidir;
- break;
-
- default:
- /* No loop: */
- sample->loopMode = sdLoopNone;
- sample->loop1Type = loopNone;
- break;
- }
-
- /* Check that the loop is legal if there is one: */
- if ( sample->loop1End == sample->loop1Start )
- {
- sample->loopMode = sdLoopNone;
- sample->loop1Type = loopNone;
- }
- }
-
- /* Add sample length to sample total: */
- smpTotal += xmSample->smpLength;
-
- /* Update sample data start offset: */
- smpStart += xmSample->smpLength;
-
- /* Skip possible unused data at the end of sample header: */
- if ( (error = fileSeek(f, xmInstSmp->headerSize -
- sizeof(xmSampleHeader), fileSeekRelative)) != OK )
- PASSERR();
-
- sample++;
- }
-
- /* Create an empty sample header if a sample was allocated but not
- loaded: */
- if ( (n != 0) && (xmInst->numSamples == 0) )
- {
- sample->name[0] = 0;
- sample->sample = NULL;
- sample->sampleType = smpNone;
- sample->volume = 0;
- sample->panning = panMiddle;
- sample->sdHandle = 0;
- }
-
- /* Seek to the start of next instrument header: */
- if ( (error = fileSeek(f, smpStart, fileSeekAbsolute)) != OK )
- PASSERR();
-
- CHECKHEAP
- }
-
-
- /* Deallocate memory instrument and sample headers: */
- if ( (error = memFree(xmInst)) != OK )
- PASSERR()
- xmInst = NULL;
- if ( (error = memFree(xmInstSmp)) != OK )
- PASSERR()
- xmInstSmp = NULL;
- if ( (error = memFree(xmSample)) != OK )
- PASSERR()
- xmSample = NULL;
-
-
- /* Check that the maximum sample length is below the Sound Device limit:*/
- if ( maxSample > SMPMAX )
- {
- ERROR(errInvalidInst, ID_gmpLoadXM);
- LoadError();
- return errInvalidInst;
- }
-
- /* If samples should be added to Sound Device, allocate memory for sample
- loading buffer: */
- if ( addSD )
- {
- if ( (error = memAlloc(maxSample, (void**) &smpBuf)) != OK )
- PASSERR()
- }
-
- /* Load all samples, convert the sample data and add the samples to Sound
- Device if necessary: */
- for ( i = 0; i < numInsts; i++ )
- {
- inst = module->instruments[i];
-
- /* Load all samples for this instrument: */
- sample = &inst->samples[0];
- for ( s = 0; s < inst->numSamples; s++ )
- {
- #ifdef DEBUGMESSAGES
- printf("Loading sample %i for instrument %i - ", s, i);
- #endif
- CHECKHEAP
-
- if ( sample->sampleType == smpNone )
- {
- #ifdef DEBUGMESSAGES
- puts("no sample");
- #endif
- /* There is no sample - just build an empty Sound Device
- sample structure: */
- sdSmp.sampleType = smpNone;
- sdSmp.samplePos = sdSmpNone;
- sdSmp.sample = NULL;
- }
- else
- {
- #ifdef DEBUGMESSAGES
- printf("sample, len = %u\n", sample->sampleLength);
- #endif
- /* There is a sample. If sample data should not be added to
- Sound Device, allocate memory for the sample data and
- point smpBuf there: */
- if ( (!addSD) && (sample->sampleLength > 0) )
- {
- smpBuf = NULL;
- if ( (error = memAlloc(sample->sampleLength, (void**)&smpBuf))
- != OK )
- PASSERR()
- sample->sample = smpBuf;
- }
- else
- {
- /* Sample is added to the Sound Device - sample data is
- not available: */
- sample->sample = NULL;
- }
-
- /* Seek to beginning of sample data in file: */
- if ( (error = fileSeek(f, sample->fileOffset,
- fileSeekAbsolute)) != OK )
- PASSERR();
-
- /* Read the sample data: */
- if ( sample->sampleLength > 0 )
- {
- if ( sample->sampleLength > maxSample )
- {
- #ifdef DEBUGMESSAGES
- puts("BIG trouble!");
- #endif
- }
- else
- {
- #ifdef DEBUGMESSAGES
- printf("Reading %i bytes to %p\n",
- sample->sampleLength, smpBuf);
- #endif
- if ( (error = fileRead(f, smpBuf,
- sample->sampleLength)) != OK)
- PASSERR();
- #ifdef DEBUGMESSAGES
- printf("Done\n");
- #endif
- CHECKHEAP
- }
- }
- else
- {
- sample->sampleType = smpNone;
- }
-
- #ifdef DEBUGMESSAGES
- printf("smpBuf = %p, len = %ld\n", smpBuf, maxSample);
- #endif
-
- #if 0 /* 16-bit sample support removed for now */
- /* The sample data is in delta format. */
- if ( sample->sampleType == smp16bit )
- {
- /* 16-bit sample */
- #ifdef DEBUGMESSAGES
- puts("16-bit sample data!");
- #endif
- sdSmp.sampleType = smp16bit;
- smpData = smpBuf;
- d = 0;
- for ( n = 0; n < (sample->sampleLength / 2); n++ )
- {
- d += (int) *((signed short*) smpData);
- *((signed short*) smpData) = d;
- smpData += 2;
- }
- CHECKHEAP
- }
- else
- {
- /* 8-bit sample */
- sdSmp.sampleType = smp8bit;
- smpData = smpBuf;
- d = 0;
- for ( n = 0; n < sample->sampleLength; n++ )
- {
- d += (int) *((signed char*) smpData);
- *smpData = (uchar) (128 + d);
- smpData++;
- }
- }
-
-
- /* Set Sound Device sample type: */
- #endif /* #if 0 */
-
- /* Code from older version added */
-
- /* The sample data is in delta format. Convert it to raw
- unsigned 8-bit data: (FIXME - 16-bit samples!) */
- if ( sample->sampleType == smp16bit )
- {
- #ifdef DEBUGMESSAGES
- puts("Converting 16-bit sample data");
- #endif
- /* 16-bit sample - convert to 8-bit on the fly */
- smpData = smpBuf;
- d = 0;
- for ( n = 0; n < (sample->sampleLength / 2); n++ )
- {
- d += (int) *((signed short*) smpData);
- smpBuf[n] = (uchar) (128 + (d >> 8));
- smpData += 2;
- }
- #ifdef DEBUGMESSAGES
- puts("Converting sample structure");
- #endif
- /* Converted to 8-bit - update sample structure: */
- sample->sampleLength /= 2;
- sample->loop1Start /= 2;
- sample->loop1End /= 2;
- sample->sampleType = smp8bit;
- /* (second loop not used) */
- CHECKHEAP
- }
- else
- {
- /* 8-bit sample */
- smpData = smpBuf;
- d = 0;
- for ( n = 0; n < sample->sampleLength; n++ )
- {
- d += (int) *((signed char*) smpData);
- *smpData = (uchar) (128 + d);
- smpData++;
- }
- }
-
- /* Set Sound Device sample type: */
- sdSmp.sampleType = smp8bit;
-
- /* end of added code */
-
- /* Set Sound Device sample position in memory: */
- sdSmp.samplePos = sdSmpConv;
-
- /* Point Sound Device sample data to sample loading buffer: */
- sdSmp.sample = smpBuf;
-
- /* Point smpBuf to NULL if the sample is not added to Sound
- Device to mark it should not be deallocated: */
- if ( !addSD )
- smpBuf = NULL;
- }
-
- sdSmp.sampleLength = sample->sampleLength;
- sdSmp.loopMode = sample->loopMode;
- sdSmp.loop1Start = sample->loop1Start;
- sdSmp.loop1End = sample->loop1End;
- sdSmp.loop1Type = sample->loop1Type;
-
- /* Set up the rest of Sound Device sample structure so that the sample
- can be added to the Sound Device: */
- if ( addSD )
- {
- CHECKHEAP
-
- #ifdef DEBUGMESSAGES
- puts("gmpSD->AddSample()");
- #endif
-
- /* Add the sample to Sound Device and store the Sound Device
- sample handle in sample->sdHandle: */
- if ( (error = gmpSD->AddSample(&sdSmp, 1, &sample->sdHandle))
- != OK)
- PASSERR()
-
- CHECKHEAP
-
- /* Call sample callback if used: */
- if ( SampleCallback != NULL )
- {
- if ( (error = SampleCallback(&sdSmp, sample)) != OK )
- PASSERR()
- }
- }
- else
- {
- /* Sample data has not been added to Sound Device: */
- sample->sdHandle = 0;
- }
- sample++;
- }
- }
-
- #ifdef DEBUGMESSAGES
- puts("Samples loaded");
- #endif
- CHECKHEAP
-
- /* Deallocate sample loading buffer if allocated: */
- if ( addSD )
- {
- if ( (error = memFree(smpBuf)) != OK )
- PASSERR()
- }
- smpBuf = NULL;
-
- /* Deallocate instrument use flags: */
- if ( (error = memFree(instUsed)) != OK )
- PASSERR()
- instUsed = 0;
-
- /* Now we are finished loading. Close module file: */
- if ( (error = fileClose(f)) != OK)
- PASSERR()
- fileOpened = 0;
-
- /* Deallocate Fasttracker 2 module header: */
- if ( (error = memFree(header)) != OK )
- PASSERR();
-
- /* Return module pointer in *module: */
- *_module = module;
-
- return OK;
- }
-
-
- /*
- * $Log: loadxm.c,v $
- * Revision 1.12 1997/01/25 15:23:16 pekangas
- * The file is now closed properly if loading terminates to an error
- *
- * Revision 1.11 1997/01/16 18:41:59 pekangas
- * Changed copyright messages to Housemarque
- *
- * Revision 1.10 1997/01/07 19:18:15 pekangas
- * Now ignores envelope loops if start position = end position (as FT2 does)
- *
- * Revision 1.9 1996/12/30 23:37:19 jpaana
- * Cleaned up LoadError
- *
- * Revision 1.8 1996/10/06 16:48:38 pekangas
- * Fixed a potential signed/unsigned mismatch in panning
- *
- * Revision 1.7 1996/09/01 15:42:18 pekangas
- * Changed to use new style slides (no more signed infobytes)
- *
- * Revision 1.6 1996/07/13 20:14:11 pekangas
- * Eliminated Visual C warnings
- *
- * Revision 1.5 1996/07/13 18:24:02 pekangas
- * Fixed to compile with Visual C
- *
- * Revision 1.4 1996/06/14 16:23:40 pekangas
- * Removed heap checking to speed up loading
- *
- * Revision 1.3 1996/05/25 15:49:57 jpaana
- * Tried to fix for Linux, succeeded too
- *
- * Revision 1.2 1996/05/24 16:20:36 jpaana
- * Fixed to work with gcc
- *
- * Revision 1.1 1996/05/22 20:49:33 pekangas
- * Initial revision
- *
- */
-